#include <QMessageBox>
#include <QUrl>
#include <QInputDialog>
#include <QList>
#include <QIcon>
#include <QString>
#include <QPixmap>
#include <QHideEvent>
#include <QKeyEvent>
#include <QTreeWidgetItemIterator>
#include <QTreeWidgetItem>
#include <QProgressBar>
#include <QDesktopServices>
#include <QFileDialog>
#include <QProcess>
#include <QStringList>
#include <QFont>
#include <QUrl>
#include <QStringList>
#include <QClipboard>

#include "formmain.h"
#include "formabout.h"
#include "formeditfeed.h"
#include "formsettings.h"
#include "formeditcategory.h"
#include "definitions.h"
#include "rssthread.h"

#include <QStaticText>
#include <QTextItem>
FormMain::FormMain(QWidget *parent) : QMainWindow(parent), ui(new Ui::FormMain) {
	ui->setupUi(this);
	setupApp();
	setupTray();
	createConnections();
	loadSettings();
	reader->loadXml(true, reader->getPath());
	reader->updateRepresentation(ui->treeFeeds);
	emit trayNeedsRefresh();
	UiFactory::displayMessage(ui->webMessages,
							  tr("Welcome to RSS Guard<br><br>Current version: %1").arg(qApp->applicationVersion()));
	qDebug() << QString("%1 %2 launched.").arg(APP_ID, qApp->applicationName());
}

FormMain::~FormMain() {
	updater->exit();
	delete ui;
	reader->deleteLater();
}

void FormMain::createConnections() {
	connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showDialogAbout()));
	connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(quit()));
	connect(ui->actionImport, SIGNAL(triggered()), this, SLOT(importFeeds()));
	connect(ui->actionExport, SIGNAL(triggered()), this, SLOT(exportFeeds()));
	connect(ui->actionSettings, SIGNAL(triggered()), this, SLOT(showDialogSettings()));
	connect(ui->actionClearMessages, SIGNAL(triggered()), this, SLOT(deleteMessages()));
	connect(ui->actionRestoreMessages, SIGNAL(triggered()), this, SLOT(restoreMessages()));
	connect(ui->actionEmptyStorage, SIGNAL(triggered()), this, SLOT(trashMessages()));
	connect(ui->actionMarkAllRead, SIGNAL(triggered()), this, SLOT(markAllRead()));
	connect(ui->actionMarkAllUnread, SIGNAL(triggered()), this, SLOT(markAllUnread()));
	connect(ui->actionMarkRead, SIGNAL(triggered()), this, SLOT(markRead()));
	connect(ui->actionMarkUnread, SIGNAL(triggered()), this, SLOT(markUnread()));
	connect(ui->actionUpdateAllFeeds, SIGNAL(triggered()), this, SLOT(updateAllFeeds()));
	connect(ui->actionUpdateOneFeed, SIGNAL(triggered()), this, SLOT(updateFeed()));
	connect(ui->actionAddFeed, SIGNAL(triggered()), this, SLOT(addFeed()));
	connect(ui->actionEditFeed, SIGNAL(triggered()), this, SLOT(editItem()));
	connect(ui->actionDeleteFeed, SIGNAL(triggered()), this, SLOT(deleteFeed()));
	connect(ui->actionDeleteMessage, SIGNAL(triggered()), this, SLOT(deleteMessage()));
	connect(ui->actionTrashItem, SIGNAL(triggered()), this, SLOT(trashItem()));

	connect(ui->actionNewTabPage, SIGNAL(triggered()), this, SLOT(openNewTabPage()));
	connect(ui->actionNewTabMsg, SIGNAL(triggered()), this, SLOT(openNewTabMsg()));
	connect(ui->actionNewBrowser, SIGNAL(triggered()), this, SLOT(openNewBrowser()));
	connect(ui->actionCopyLinks, SIGNAL(triggered()), this, SLOT(copyIntoClipboard()));

	connect(ui->treeFeeds, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editItem()));

	connect(timer, SIGNAL(timeout()), this, SLOT(timerTick()));
	connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
	connect(txtSearch, SIGNAL(textChanged(QString)), this, SLOT(filterMessages()));
	connect(ui->tabMain, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));

	connect(this, SIGNAL(listNeedsRefresh()), this, SLOT(refreshList()));
	connect(this, SIGNAL(trayNeedsRefresh()), this, SLOT(refreshTrayIcon()));
	connect(tray, SIGNAL(trayHovered()), this, SLOT(refreshTrayTooltip()));

	// Update look of feed widget when any feed is updated.
	connect(updater, SIGNAL(progress(int,QString)), this, SLOT(updateDisplay(int,QString)));
	// Update look and tooltip of trayicon when any feed is updated.
	connect(updater, SIGNAL(progress(int,QString)), this, SLOT(refreshTrayIcon()));
	// Hide status widgets when updating finished.
	connect(updater, SIGNAL(updateFinished()), this, SLOT(hideStatus()));
	// Display bubble with info about new feeds.
	connect(updater, SIGNAL(updateFinished()), this, SLOT(updateListTray()));

	connect(ui->treeFeeds, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(feedSelectionChanged()));
	connect(ui->treeFeeds, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), txtSearch, SLOT(clear()));
	connect(ui->treeFeeds, SIGNAL(mousePressed(QMouseEvent*)), this, SLOT(contextMenuFeed(QMouseEvent*)));
	connect(ui->treeFeeds, SIGNAL(keyPressed(QKeyEvent*)), this, SLOT(shortcutFeeds(QKeyEvent*)));

	connect(ui->treeMessages, SIGNAL(mousePressed(QMouseEvent*)), this, SLOT(contextMenuMessage(QMouseEvent*)));
	connect(ui->treeMessages, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(expandMessage()));
	connect(ui->treeMessages, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(displayMessage(QTreeWidgetItem*,QTreeWidgetItem*)));
	connect(ui->treeMessages, SIGNAL(keyPressed(QKeyEvent*)), this, SLOT(shortcutMessages(QKeyEvent*)));
}

void FormMain::closeTab(int index) {
	if (index) {
		delete ui->tabMain->widget(index);
	}
}

bool FormMain::isFeedSelected() {
	return (ui->treeFeeds->selectedItems().size() && !ui->treeFeeds->selectedItems().at(0)->childCount());
}

bool FormMain::isCategorySelected() {
	return (ui->treeFeeds->selectedItems().size() && ui->treeFeeds->selectedItems().at(0)->childCount());
}

bool FormMain::isMessageSelected() {
	return (ui->treeMessages->selectedItems().size());
}

void FormMain::checkForUpdate() {
	QString version = AppFactory::latestVersion(APP_URL);
	if (version != APP_VERSION)
		tray->showMessage(APP_NAME, tr("You are not using latest stable version. Latest stable version is %1. Your version is %2.").arg(version,
																																		APP_VERSION));
}

void FormMain::hideStatus() {
	bar->hide();
	statusText->hide();
	ui->actionQuit->setEnabled(true);
}

void FormMain::showStatus(int barMax) {
	ui->actionQuit->setEnabled(false);
	bar->setVisible(true);
	statusText->setVisible(true);
	bar->setValue(0);
	bar->setMaximum(barMax);
}

/*
 0 - Display all balloon tips.
 1 - Display only when new messages are available.
 2 - Never display balloon tips.
*/
void FormMain::updateListTray() {
	switch (AppFactory::settings->value("displayUpdateInfo", 0).toInt()) {
		case 1: {
				int countOfSeen = reader->countOfUnseenMessages();
				if (countOfSeen > 0)
					tray->showMessage(APP_NAME, tr("RSS Guard updated feeds and discovered, that there are unread messages.\nYou have %2 unread messages.").arg(QString::number(countOfSeen)), QSystemTrayIcon::Information);
			}
			break;
		case 2:
			break;
		default: {
				int countOfSeen = reader->countOfUnseenMessages();
				if (countOfSeen > 0)
					tray->showMessage(APP_NAME, tr("RSS Guard updated feeds and discovered, that there are unread messages.\nYou have %2 unread messages.").arg(QString::number(countOfSeen)), QSystemTrayIcon::Information);
				else if (reader->getFeeds()->size())
					tray->showMessage(APP_NAME, tr("RSS Guard updated its feeds and did not find any new unread messages."), QSystemTrayIcon::Information);
			}
			break;

	}
}

void FormMain::updateDisplay(int index, const QString &title) {
	statusText->setText(tr("Updating feed: %1").arg(title));
	statusText->adjustSize();
	bar->setValue(index);

	reader->updateColors(ui->treeFeeds);

	if (isFeedSelected() && ui->treeFeeds->selectedItems().at(0)->text(0) == title) {
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
	}
	else if (isCategorySelected()) {
		QList<RssFeed*> feeds = reader->getFeedsByCategory(ui->treeFeeds->selectedItems().at(0)->text(0));
		ui->treeMessages->clear();
		foreach (RssFeed *feed, feeds)
			feed->updateRepresentation(ui->treeMessages, true);
	}
}

void FormMain::shortcutFeeds(QKeyEvent *event) {
	if (event->key() == Qt::Key_Delete)
		deleteFeed();
}

void FormMain::shortcutMessages(QKeyEvent *event) {
	if (event->key() == Qt::Key_Delete) {
		deleteMessage();
	}
	else if (event->key() == Qt::Key_Return) {
		expandMessage();
	}
}

void FormMain::contextMenuMessage(QMouseEvent *event) {
	// right-mouse button was clicked and at least one message is selected
	if (event->button() == Qt::RightButton && ui->treeMessages->itemAt(event->pos())) {
		// open message in external browser on right-click
		if (AppFactory::settings->value("rightClickbrowser", false).toBool()) {
			goToLink(QStringList(ui->treeMessages->itemAt(event->pos())->toolTip(0)));
		}
		// display context menu
		else {
			QMenu menu(this);
			menu.addAction(ui->actionNewTabPage);
			menu.addAction(ui->actionNewTabMsg);
			menu.addAction(ui->actionNewBrowser);
			menu.addAction(ui->actionCopyLinks);
			menu.addAction(ui->actionDeleteMessage);
			menu.exec(event->globalPos());
		}
	}
}

void FormMain::contextMenuFeed(QMouseEvent *event) {
	feedSelectionChanged(event->pos());
	if (event->button() == Qt::RightButton) {
		if (ui->treeFeeds->itemAt(event->pos())) {
			QMenu menu(this);

			menu.addAction(ui->actionUpdateOneFeed);
			menu.addAction(ui->actionMarkRead);
			menu.addAction(ui->actionMarkUnread);
			menu.addSeparator();
			menu.addAction(ui->actionAddFeed);
			menu.addAction(ui->actionEditFeed);
			menu.addAction(ui->actionDeleteFeed);
			menu.addAction(ui->actionTrashItem);
			menu.exec(event->globalPos());
		}
		else {
			QMenu menu(this);

			menu.addAction(ui->actionUpdateAllFeeds);
			menu.addSeparator();
			menu.addAction(ui->actionAddFeed);
			menu.addAction(ui->actionImport);
			menu.addAction(ui->actionExport);
			menu.exec(event->globalPos());
		}
	}
}

void FormMain::refreshList() {
	reader->updateColors(ui->treeFeeds);
	emit trayNeedsRefresh();
}

void FormMain::filterMessages() {
	QTreeWidgetItemIterator it(ui->treeMessages);
	while (*it) {
		if ((*it)->text(0).contains(txtSearch->text(), Qt::CaseInsensitive))
			(*it)->setHidden(false);
		else
			(*it)->setHidden(true);
		*it++;
	}
}

void FormMain::displayMessage(QTreeWidgetItem *current, QTreeWidgetItem *previous) {
	Q_UNUSED(previous)

	if (AppFactory::settings->value("centerSelection", true).toBool())
		ui->treeMessages->scrollToItem(current, QAbstractItemView::PositionAtCenter);

	if (current) {
		RssItem *item = reader->getItemByLink(current->toolTip(0));
		if (item) {
			UiFactory::displayMessage(ui->webMessages,
									  item->getTitle(),
									  item->getDescription());
			item->setSeen(true);
		}
		current->setFont(0, QFont());
		current->setIcon(0, QIcon(":/graph/resources/graphics/state-normal.png"));
	}
	emit listNeedsRefresh();
}

void FormMain::openNewTabMsg() {
	foreach (QTreeWidgetItem *item, ui->treeMessages->selectedItems()) {
		QWebView *view = new QWebView(this);
		AppFactory::applyWebSettings(AppFactory::webSettings, view);
		ui->tabMain->addTab(view, item->text(0).size() > LIMIT_TRIM_TITLE ?
								item->text(0).left(LIMIT_TRIM_TITLE - 3) + "..." :
								item->text(0));
		RssItem *msg = reader->getItemByLink(item->toolTip(0));
		UiFactory::displayMessage(view,
								  msg->getTitle(),
								  msg->getDescription());
	}
}

void FormMain::openNewTabPage() {
	foreach (QTreeWidgetItem *item, ui->treeMessages->selectedItems()) {
		QWebView *view = new QWebView(this);
		AppFactory::applyWebSettings(AppFactory::webSettings, view);
		ui->tabMain->addTab(view, item->text(0).size() > LIMIT_TRIM_TITLE ?
								item->text(0).left(LIMIT_TRIM_TITLE - 3) + "..." :
								item->text(0));
		view->load(QUrl(item->toolTip(0)));
	}
}

void FormMain::openNewBrowser() {
	QStringList list;
	foreach (QTreeWidgetItem *item, ui->treeMessages->selectedItems()) {
		list.append(item->toolTip(0));
	}
	goToLink(list);
}

void FormMain::copyIntoClipboard() {
	QStringList list;
	foreach (QTreeWidgetItem *item, ui->treeMessages->selectedItems()) {
		list.append(item->toolTip(0));
	}
	qApp->clipboard()->setText(list.join("\n"));
}

void FormMain::expandMessage() {
	// open message in new tab
	if (AppFactory::settings->value("useNewTab", true).toBool()) {
		if (AppFactory::settings->value("openWebsite", true).toBool()) {
			openNewTabPage();
		}
		else {
			openNewTabMsg();
		}
	}
	// open message in browser
	else {
		openNewBrowser();
	}
}

void FormMain::goToLink(const QStringList &links) {
	bool customBrowser = AppFactory::settings->value("useBrowser", false).toBool() && QFile(AppFactory::settings->value("browser", "").toString()).exists();
	QString args;
	foreach (QString link, links) {
		if (customBrowser) {
			args = "\"" + AppFactory::settings->value("browser", "").toString() +
				   "\" " +
				   AppFactory::settings->value("browserArgs", DEFAULT_ARGS).toString().replace(DEFAULT_ARGS, link);
			QProcess browser;
			browser.startDetached(args);
		}
		else
			QDesktopServices::openUrl(link);
	}
}

void FormMain::dragEnterEvent(QDragEnterEvent * event) {
	if (event->mimeData()->hasFormat("text/plain"))
		event->acceptProposedAction();
}

void FormMain::dropEvent(QDropEvent *event) {
	addFeed(event->mimeData()->text());
}

void FormMain::keyPressEvent(QKeyEvent *event) {
	// Inserting text into main form triggers adding of new feed.
	if (event->type() == QKeyEvent::KeyPress && event->matches(QKeySequence::Paste)) {
		addFeed(QApplication::clipboard()->text());
	}
	// CTRL is triggered.
	else if (event->modifiers() == Qt::ControlModifier) {
		// Switching among opened tabs.
		if (event->key() == Qt::Key_Tab) {
			int index = ui->tabMain->currentIndex();
			int count = ui->tabMain->count();
			if (index == count-1) {
				ui->tabMain->setCurrentIndex(0);
			}
			else {
				ui->tabMain->setCurrentIndex(index+1);
			}
		}
		// Activate messages treewidget.
		else if (event->key() == Qt::Key_Right) {
			if (!ui->tabMain->currentIndex()) {
				ui->treeMessages->raise();
				ui->treeMessages->setFocus();
				// Little hack, so that when switched to treeMessages, user can hit ENTER.
				ui->treeMessages->setCurrentIndex(ui->treeMessages->currentIndex());
                ui->treeMessages->scrollToTop();
			}
		}
        // Activate feeds treewidget.
		else if (event->key() == Qt::Key_Left) {
			if (!ui->tabMain->currentIndex()) {
				ui->treeFeeds->raise();
				ui->treeFeeds->setFocus();
				// Little hack too.
				ui->treeFeeds->setCurrentIndex(ui->treeFeeds->currentIndex());
			}
		}
	}
	// Close tab with ESC key.
	else if (event->key() == Qt::Key_Escape) {
		int index = ui->tabMain->currentIndex();
		if (index) {
			delete ui->tabMain->widget(index);
		}
		if (!ui->tabMain->currentIndex()) {
			ui->treeMessages->raise();
			ui->treeMessages->setFocus();
		}
	}
	QMainWindow::keyPressEvent(event);
}

void FormMain::deleteMessage() {
	if (isMessageSelected()) {
		QList<QTreeWidgetItem*> list = ui->treeMessages->selectedItems();
		for (int i = 0; i < list.size(); i++) {
			reader->getItemByLink(list.at(i)->toolTip(0))->setHidden(true);
			delete list.at(i);
		}
	}
	emit listNeedsRefresh();
}

// clear all messages from feed or category (they will be downloaded
// again in case if they are still in online feed)
void FormMain::trashItem() {
	if (isFeedSelected()) {
		RssFeed *feed = reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0));
		feed->trashMessages();
		ui->treeMessages->clear();
		emit listNeedsRefresh();
	}
	else if (isCategorySelected()) {
		QList<RssFeed*> feeds = reader->getFeedsByCategory(ui->treeFeeds->selectedItems().at(0)->text(0));
		foreach (RssFeed *feed, feeds) {
			feed->trashMessages();
		}
		ui->treeMessages->clear();
		emit listNeedsRefresh();
	}
}

void FormMain::deleteFeed() {
	if ((isCategorySelected() || isFeedSelected())) {
		bool feedSel = isFeedSelected() ? true : false;
		QString key(ui->treeFeeds->selectedItems().at(0)->text(0));
		ui->treeMessages->clear();
		UiFactory::displayMessage(ui->webMessages,
								  tr("No message selected"));
		if (feedSel) {
			reader->removeFeed(key);
		}
		else  {
			reader->removeCategory(key);
		}
	}
	reader->updateRepresentation(ui->treeFeeds);
	emit trayNeedsRefresh();
}

void FormMain::importFeeds() {
	QString selected;
	QString fileName = QFileDialog::getOpenFileName(this, tr("Import file"), QDir::homePath(),
													"OPML 1.0 (*.opml *.xml);;RSS Guard XML (*.rgx);;", &selected);
	if (!fileName.size()) return;
	QMessageBox msgBox;
	msgBox.setText(tr("Do you want to append imported feeds to existing ones or delete them? Use Yes option to append or No to delete"));
	msgBox.setWindowTitle(tr("Append or delete"));
	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
	msgBox.setDefaultButton(QMessageBox::Yes);
	int ret = msgBox.exec();
	bool append = false;
	if (ret == QMessageBox::Yes)
		append = true;
	if (selected == "OPML 1.0 (*.opml *.xml)")
		reader->loadOpml(append, fileName);
	else if (selected == "RSS Guard XML (*.rgx)")
		reader->loadXml(append, fileName);
	reader->updateRepresentation(ui->treeFeeds);
	emit trayNeedsRefresh();
}

void FormMain::exportFeeds() {
	QString selected;
	QString fileName = QFileDialog::getSaveFileName(this, tr("Export file"), QDir::homePath(),
													"OPML 1.0 (*.opml *.xml);;RSS Guard XML (*.rgx);;", &selected);
	if (!fileName.size()) return;
	if (selected == "OPML 1.0 (*.opml *.xml)")
		reader->saveOpml(fileName);
	else if (selected == "RSS Guard XML (*.rgx)")
		reader->saveXml(fileName);
}

void FormMain::showDialogAbout() {
	FormAbout *aboutDialog = new FormAbout(":/text/resources/text/COPYING", ":/text/resources/text/AUTHORS", ":/text/resources/text/CHANGELOG", this);
	aboutDialog->show();
}

void FormMain::switchWindow() {
	if (isVisible())
		hide();
	else {
		showWindow();
	}
}

void FormMain::setReaderPath() {
	QString defaultPath = AppFactory::settings->value("feedFileNamePath", CONFIG_UNKNOWN).toString();

	// if there is defined custom path to file containing feeds, then use it
	if (defaultPath != CONFIG_UNKNOWN)
		reader->setPath(defaultPath);
	// or if custom path is not defined and feeds.xml file exists in executable file director, then use this one
	else if (QFile(qApp->applicationDirPath() + QDir::separator() + APP_PATH_FEEDS).exists())
		reader->setPath(qApp->applicationDirPath() + QDir::separator() + APP_PATH_FEEDS);
	// if custom path is not defined and no feeds.xml file exists, then use file in default path (homePath())
	else
		reader->setPath(APP_PATH_BASE + QDir::separator() + APP_PATH_FEEDS);
}

void FormMain::setupApp() {

#ifdef Q_WS_MAC
	setUnifiedTitleAndToolBarOnMac(true);
	// I'm real coward facing Cocoa/Carbon hell to get dock event
	// to reopen already closed window. Sorry. No way to implement
	// monster like this:
	// https://github.com/KDAB/Charm/blob/master/Charm/MacApplication.mm
	qApp->setQuitOnLastWindowClosed(true);
#else
	qApp->setQuitOnLastWindowClosed(false);
#endif

	ui->treeFeeds->setStyleSheet("QTreeWidget:branch:!has-children { border-image: none; border: 0px transparent; }");

	reader = new RssReader(APP_PATH_BASE + QDir::separator() + APP_PATH_FEEDS);
	setReaderPath();

	timer = new Timer(this);
	timer->setInterval(2000);

	bar = new QProgressBar(this);
	bar->hide();
	ui->statusBar->addWidget(bar);
	bar->setSizePolicy(QSizePolicy());

	statusText = new QLabel(this);
	statusText->setWordWrap(false);
	statusText->hide();

	ui->statusBar->addWidget(statusText);

	txtSearch = new LineEdit(this, tr("Search"));

	spacerWidget = new QWidget(this);
	spacerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);

	ui->toolBar->addWidget(spacerWidget);
	ui->toolBar->addWidget(txtSearch);
	txtSearch->setStyleSheet("margin-right: 5px;");
	txtSearch->setMinimumWidth(160);
	txtSearch->setMaximumWidth(160);
	spacerWidget->setVisible(true);
	txtSearch->setVisible(true);

	trayHasntNew = QIcon(":/graph/resources/graphics/rssguard.png");
	trayFont.setBold(true);
	trayFont.setPointSize(PEN_DEFAULT);
	trayPen = QPen(Qt::white);
	trayRectangle = QRect(10, 0, 108, 110);

	ui->tabMain->tabBar()->setMidButtonCloseable(AppFactory::settings->value("tabsMidButton", true).toBool());

	updater = new UpdateThread(this);

	AppFactory::setupWebSettings(ui->webMessages->settings());
}

void FormMain::refreshTrayIcon() {
	int unreadCount = reader->countOfUnseenMessages();
	if (unreadCount) {
		QPixmap icon = QPixmap(":/graph/resources/graphics/rssguard-active.png");
		trayPainter.begin(&icon);
		trayPainter.setPen(trayPen);
		if (unreadCount >= 999) {
			trayFont.setPointSize(PEN_BIGGER);
			trayPainter.setFont(trayFont);
			trayPainter.drawText(trayRectangle, Qt::AlignVCenter | Qt::AlignCenter , "∞");
		}
		else {
			trayFont.setPointSize(PEN_DEFAULT);
			trayPainter.setFont(trayFont);
			trayPainter.drawText(trayRectangle, Qt::AlignVCenter | Qt::AlignCenter , QString::number(reader->countOfUnseenMessages()));
		}
		trayPainter.end();
		// no tray on mac - only dock
#ifdef Q_OS_MAC
		qApp->setWindowIcon(icon);
#else
		tray->setIcon(icon);
#endif
	}
	else {
		// no tray on mac - only dock
#ifdef Q_OS_MAC
		qApp->setWindowIcon(trayHasntNew);
#else
		tray->setIcon(trayHasntNew);
#endif
	}
#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
	refreshTrayTooltip();
#endif
}

void FormMain::refreshTrayTooltip() {
	QStringList list;
	list << ":/graph/resources/graphics/rssguard-small.png" << APP_NAME << tr("Unread messages: %1").arg(QString::number(reader->countOfUnseenMessages()));
#if defined(Q_WS_X11)
	if (AppFactory::settings->value("updateAutomatically", true).toBool())
		list << tr("Next update (minutes): %1").arg(timer->timeLeft());
#endif
	tray->setToolTip(list);
}


void FormMain::setupTray() {
	tray = new SystemTrayIcon(QIcon(":/graph/resources/graphics/rssguard.png"), this);
	// Mac OS X hack here. It looks like it's better not to
	// show tray icon on mac because it's functionality duplicates
	// the Dock only - there is no additional stuff.
	// Also the "count of unread news" is almost unreadable in the tray
	// but it works perfectly in the dock.
#ifndef Q_OS_MAC
	tray->setToolTip(APP_NAME);
	if (!QSystemTrayIcon::isSystemTrayAvailable()) {
		QMessageBox::critical(0, APP_NAME, tr("Your system doesn't support tray icon. RSS Guard will start in signle-window mode."));
		qWarning() << QString("%1 Your system doesn't support tray icon. RSS Guard will start in single-window mode.").arg(APP_ID);
		qApp->setQuitOnLastWindowClosed(true);
		return;
	}
	setupTrayMenu(tray);
	tray->show();
#endif
}

void FormMain::setupTrayMenu(QSystemTrayIcon *tray) {
	QMenu *menu = new QMenu(this);
	menu->addAction(ui->actionUpdateAllFeeds);
	menu->addAction(ui->actionMarkAllRead);
	menu->addSeparator();
	menu->addAction(ui->actionSettings);
	menu->addAction(ui->actionQuit);
	tray->setContextMenu(menu);
}

void FormMain::trayActivated(QSystemTrayIcon::ActivationReason r) {
#ifndef Q_WS_MAC
	switch (r) {
		case QSystemTrayIcon::Trigger:
			switchWindow();
			break;
		default:
			break;
	}
#else
	// thx to Petr Vanek
	Q_UNUSED(r);
	setWindowState(Qt::WindowActive);
	show();
#endif
}

void FormMain::showWindow() {
	show();
	activateWindow();
	raise();
}

void FormMain::quit() {
	tray->hide();
	updater->exit();

	if (AppFactory::settings->value("deleteReadOnExit", false).toBool())
		reader->hideMessages(true);

	saveSettings();
	if (!reader->saveXml(reader->getPath())) {
		QMessageBox::warning(this, tr("Feeds weren't saved"), tr("Feeds couldn't be save in local storage file, because file is probably read-only."));
		qWarning() << QString("%1 Feeds couldn't be saved in local storage file, because file is probably read-only.").arg(APP_ID);
	}
	qDebug() << QString("%1 %2 is quitting.").arg(APP_ID, qApp->applicationName());
	QApplication::quit();
}

void FormMain::saveSettings() {
	AppFactory::settings->beginGroup("Geometry");
	AppFactory::settings->setValue("windowState", saveState());
	AppFactory::settings->setValue("windowGeometry", saveGeometry());
	AppFactory::settings->setValue("splitMainGeometry", ui->splitMain->saveGeometry());
	AppFactory::settings->setValue("splitMainState", ui->splitMain->saveState());
	AppFactory::settings->setValue("splitMessagesGeometry", ui->splitMessages->saveGeometry());
	AppFactory::settings->setValue("splitMessagesState", ui->splitMessages->saveState());
	AppFactory::settings->endGroup();

	AppFactory::settings->setValue("treeFeedsFirstColumn", ui->treeFeeds->columnWidth(0));

	AppFactory::settings->setValue("messagesSortingColumn", ui->treeMessages->sortColumn());

	AppFactory::settings->setValue("messagesGeometry", ui->treeMessages->saveGeometry());

	// headers in trees
	AppFactory::settings->beginGroup("TreeHeaders");
	AppFactory::settings->remove("");
	// feeds
	AppFactory::settings->beginWriteArray("feeds");
	for(int i = 0; i < ui->treeFeeds->columnCount(); ++i) {
		AppFactory::settings->setArrayIndex(i);
		AppFactory::settings->setValue("hidden", ui->treeFeeds->isColumnHidden(i));
		AppFactory::settings->setValue("width", ui->treeFeeds->columnWidth(i));
	}
	AppFactory::settings->endArray();
	// messages
	AppFactory::settings->beginWriteArray("messages");
	for(int i = 0; i < ui->treeMessages->columnCount(); ++i) {
		AppFactory::settings->setArrayIndex(i);
		AppFactory::settings->setValue("hidden", ui->treeMessages->isColumnHidden(i));
		AppFactory::settings->setValue("width", ui->treeMessages->columnWidth(i));
	}
	AppFactory::settings->endArray();
	AppFactory::settings->endGroup();
	//

	AppFactory::settings->beginGroup("InternalBrowser");
	AppFactory::settings->setValue("StandardFontFamily", AppFactory::webSettings->fontFamily(QWebSettings::StandardFont));
	AppFactory::settings->setValue("StandardFontSize", AppFactory::webSettings->fontSize(QWebSettings::DefaultFontSize));
	AppFactory::settings->endGroup();
	AppFactory::checkSettings();
}

void FormMain::loadSettings() {
	AppFactory::checkSettings();
	if (AppFactory::settings->value("firstRun", true).toBool()) {
		QMessageBox msgBox;
		msgBox.setText(tr("Hello! Welcome in RSS Guard. RSS Guard is (very) tiny feed reader.\n\n") +
					   tr("If you want to get further information, then use Help menu item.\n\n") +
					   tr("You can change language of RSSG in Settings menu section.\n\n") +
					   tr("Remember that RSSG is open-source software, so you can get source code for it and use it under GNU GPLv3 licence only. For further information visit Help section too.\n\n\nCopyright ©  2011 Martin Rotter"));
		msgBox.setStandardButtons(QMessageBox::Ok);
		msgBox.setIconPixmap(QPixmap(":/graph/resources/graphics/RssGuard.png"));
		msgBox.exec();
		AppFactory::settings->setValue("firstRun", false);
	}
	if (AppFactory::settings->value("updateAutomatically", true).toBool())
		timer->start(AppFactory::settings->value("updateInterval", 1800000).toInt());

	AppFactory::settings->beginGroup("Geometry");
	restoreState(AppFactory::settings->value("windowState").toByteArray());
	restoreGeometry(AppFactory::settings->value("windowGeometry").toByteArray());
	ui->splitMain->restoreGeometry(AppFactory::settings->value("splitMainGeometry").toByteArray());
	ui->splitMain->restoreState(AppFactory::settings->value("splitMainState").toByteArray());
	ui->splitMessages->restoreGeometry(AppFactory::settings->value("splitMessagesGeometry").toByteArray());
	ui->splitMessages->restoreState(AppFactory::settings->value("splitMessagesState").toByteArray());
	AppFactory::settings->endGroup();

	ui->treeFeeds->setColumnWidth(0, AppFactory::settings->value("treeFeedsFirstColumn").toInt());
	ui->treeMessages->sortByColumn(AppFactory::settings->value("messagesSortingColumn", 1).toInt());
	ui->treeMessages->restoreGeometry(AppFactory::settings->value("messagesGeometry").toByteArray());

	// headers in trees
	AppFactory::settings->beginGroup("TreeHeaders");
	// feeds
	int count = AppFactory::settings->beginReadArray("feeds");
	for(int i = 0; i < count; ++i) {
		AppFactory::settings->setArrayIndex(i);
		// main column has to be always visible
		ui->treeFeeds->setColumnHidden(i, i == 0 ? false : AppFactory::settings->value("hidden", true).toBool());
		ui->treeFeeds->setColumnWidth(i, AppFactory::settings->value("width", DEF_WIDTH).toInt());
	}
	AppFactory::settings->endArray();
	// messages
	count = AppFactory::settings->beginReadArray("messages");
	for(int i = 0; i < count; ++i) {
		AppFactory::settings->setArrayIndex(i);
		// main column has to be always visible
		ui->treeMessages->setColumnHidden(i, i == 0 ? false : AppFactory::settings->value("hidden", true).toBool());
		ui->treeMessages->setColumnWidth(i, AppFactory::settings->value("width", DEF_WIDTH).toInt());
	}
	AppFactory::settings->endArray();
	AppFactory::settings->endGroup();
	//

	AppFactory::settings->beginGroup("InternalBrowser");
	AppFactory::webSettings->setFontFamily(QWebSettings::StandardFont,
										   AppFactory::settings->value("StandardFontFamily",
																	   AppFactory::webSettings->fontFamily(QWebSettings::StandardFont)).toString());
	AppFactory::webSettings->setFontSize(QWebSettings::DefaultFontSize,
										 AppFactory::settings->value("StandardFontSize",
																	 AppFactory::webSettings->fontSize(QWebSettings::DefaultFontSize)).toInt());
	AppFactory::settings->endGroup();

	AppFactory::setApplicationProxy();
}

void FormMain::expandTreeWidget(QTreeWidget *widget) {
	QTreeWidgetItemIterator it(widget);
	while (*it) (*it++)->setExpanded(true);
}

void FormMain::addFeed(const QString &link) {
	QString suggestedCategory = ui->treeFeeds->currentTopLevelText();
	FormEditFeed feedDialog(reader, this, suggestedCategory, link);
	RssFeed *feed = feedDialog.editFeed();
	if (feed) {
		reader->addFeed(feed);
		reader->updateRepresentation(ui->treeFeeds);
		ui->treeMessages->clear();
		if (AppFactory::settings->value("updateAfter", false).toBool())
			updateFeed(feed);
		if (isFeedSelected())
			reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
		emit listNeedsRefresh();
		UiFactory::displayMessage(ui->webMessages,
								  tr("No message selected"));
	}
}

void FormMain::feedSelectionChanged(const QPoint &where) {
	if (isFeedSelected()) {
		ui->actionAddFeed->setText(tr("Add feed"));
		ui->actionAddFeed->setToolTip(tr("Add feed"));
		ui->actionMarkRead->setText(tr("Mark feed as read"));
		ui->actionMarkRead->setToolTip(tr("Mark this feed as read"));
		ui->actionMarkUnread->setText(tr("Mark feed as unread"));
		ui->actionMarkUnread->setToolTip(tr("Mark this feed as unread"));

		ui->actionTrashItem->setText(tr("Trash messages from feed"));
		ui->actionTrashItem->setToolTip(tr("This will permanently erase all messages from this feed, resulting in smaller storage file."
										   "Note that these messages will be downloaded again if still available in online feed."));


		ui->actionUpdateOneFeed->setText(tr("Update feed"));
		ui->actionUpdateOneFeed->setToolTip(tr("Update this feed"));
		ui->actionEditFeed->setText(tr("Edit feed"));
		ui->actionDeleteFeed->setText(tr("Delete feed"));
		ui->actionEditFeed->setToolTip(tr("Edit this feed"));
		ui->actionDeleteFeed->setToolTip(tr("Delete this feed"));
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
		UiFactory::displayMessage(ui->webMessages,
								  tr("No message selected"));
	}
	else if (isCategorySelected()) {
		if (ui->treeMessages->itemAt(where)) {
			ui->actionAddFeed->setText(tr("Add feed to this category"));
			ui->actionAddFeed->setToolTip(tr("Add feed to this category"));
		}
		ui->actionMarkRead->setText(tr("Mark category as read"));
		ui->actionMarkRead->setToolTip(tr("Mark this category as read"));
		ui->actionMarkUnread->setText(tr("Mark category as unread"));
		ui->actionMarkUnread->setToolTip(tr("Mark this category as unread"));

		ui->actionTrashItem->setText(tr("Trash messages from category"));
		ui->actionTrashItem->setToolTip(tr("This will permanently erase all messages from this category, resulting in smaller storage file."
										   "Note that these messages will be downloaded again if still available in online feed from this category."));

		ui->actionUpdateOneFeed->setText(tr("Update category"));
		ui->actionUpdateOneFeed->setToolTip(tr("Update this category"));
		ui->actionEditFeed->setText(tr("Edit category"));
		ui->actionDeleteFeed->setText(tr("Delete category"));
		ui->actionEditFeed->setToolTip(tr("Edit this category"));
		ui->actionDeleteFeed->setToolTip(tr("Delete this category"));
		QList<RssFeed*> feeds = reader->getFeedsByCategory(ui->treeFeeds->selectedItems().at(0)->text(0));
		ui->treeMessages->clear();
		foreach (RssFeed *feed, feeds) {
			feed->updateRepresentation(ui->treeMessages, true);
		}
	}
}

void FormMain::editItem() {
	if (isFeedSelected()) {
		FormEditFeed feedDialog(reader, this);
		RssFeed *oldFeed = reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0));
		QString oldLink = oldFeed->getLink();
		QString oldEnc = oldFeed->getEncoding();
		RssFeed  *newFeed = feedDialog.editFeed(oldFeed);
		if (newFeed == NULL) return;
		QString newLink = newFeed->getLink();
		QString newEnc = newFeed->getEncoding();
		if (newLink != oldLink || oldEnc != newEnc) {
			QMessageBox::information(this, tr("Feed's link or encoding were changed"), tr("You have changed link or encoding in one of your feeds. All messages from this feed were permanently erased. Please update your feed to get new ones."));
			ui->treeMessages->clear();
		}
		reader->editFeed(oldFeed, newFeed);
		if (AppFactory::settings->value("updateAfter", false).toBool())
			updateFeed(oldFeed);
		reader->updateRepresentation(ui->treeFeeds);
		emit listNeedsRefresh();
	}
	else if (isCategorySelected()) {
		FormEditCategory formCategory(ui->treeFeeds->selectedItems().at(0)->text(0) ,this);
		QString newName;
		if (formCategory.getText(&newName)) {
			reader->editCategory(ui->treeFeeds->selectedItems().at(0)->text(0), newName);
			reader->updateRepresentation(ui->treeFeeds);
		}
	}
}

void FormMain::markRead() {
	if (isFeedSelected()) {
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->markAllVisibleMessages(true);
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
		emit listNeedsRefresh();
	}
	else if (isCategorySelected())
		markCategoryRead();
}

void FormMain::markUnread() {
	if (isFeedSelected()) {
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->markAllVisibleMessages(false);
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
		emit listNeedsRefresh();
	}
	else if (isCategorySelected())
		markCategoryUnread();
}

void FormMain::markAllRead() {
	reader->markReader(true);
	if (isFeedSelected())
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
	emit listNeedsRefresh();
}

void FormMain::markAllUnread() {
	reader->markReader(false);
	if (isFeedSelected())
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
	emit listNeedsRefresh();
}

void FormMain::deleteMessages() {
	QMessageBox msgBox;
	msgBox.setText(tr("Do you want to delete read messages only?"));
	msgBox.setInformativeText(tr("If you want to delete just read messages, then use Yes as your answer. But if you want to dellete all (unread too) messages, then select No as your answer"));
	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
	msgBox.setDefaultButton(QMessageBox::Yes);
	int result = msgBox.exec();
	if (result == QMessageBox::Yes) {
		reader->hideMessages(true);
		ui->treeMessages->clear();
		if (isFeedSelected())
			reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
		UiFactory::displayMessage(ui->webMessages,
								  tr("No message selected"));
	}
	else if (result == QMessageBox::No) {
		reader->hideMessages(false);
		ui->treeMessages->clear();
		UiFactory::displayMessage(ui->webMessages,
								  tr("No message selected"));
	}
	emit listNeedsRefresh();
}

void FormMain::restoreMessages() {
	QMessageBox msgBox;
	msgBox.setText(tr("Do you want to restore all deleted messages?"));
	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
	msgBox.setDefaultButton(QMessageBox::Yes);
	int result = msgBox.exec();
	if (result == QMessageBox::Yes)
		reader->unhideMessages();
	if (isFeedSelected())
		reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0))->updateRepresentation(ui->treeMessages);
	emit listNeedsRefresh();
}


void FormMain::trashMessages() {
	QMessageBox msgBox;
	msgBox.setText(tr("Do you want to clear local messages storage? If you will do this, then local configuration file will be cleaner and all messages will be downloaded again on next feed update."));
	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
	msgBox.setDefaultButton(QMessageBox::Yes);
	int result = msgBox.exec();
	if (result == QMessageBox::Yes) {
		reader->trashAllMessages();
		ui->treeMessages->clear();
		UiFactory::displayMessage(ui->webMessages,
								  tr("No message selected"));
	}
	emit listNeedsRefresh();
}

void FormMain::timerTick() {
	updateAllFeeds();
}

void FormMain::showDialogSettings() {
	QString lang = AppFactory::settings->value("currentLanguage", "rssguard_en.qm").toString();
	bool automat = AppFactory::settings->value("updateAutomatically", true).toBool();
	int interv = AppFactory::settings->value("updateInterval", 1800000).toInt();
	FormSettings form(this);
	if (form.exec() == QDialog::Accepted) {
		if ((AppFactory::settings->value("updateAutomatically", true).toBool() == true) &&
				((AppFactory::settings->value("updateInterval", 1800000).toInt() != interv) ||
				 (AppFactory::settings->value("updateAutomatically", true).toBool() == !automat))) {
			timer->start(AppFactory::settings->value("updateInterval", 1800000).toInt());
		}
		else if (AppFactory::settings->value("updateAutomatically", true).toBool() == false)
			timer->stop();
		setReaderPath();
		AppFactory::setApplicationProxy();
		ui->tabMain->tabBar()->setMidButtonCloseable(AppFactory::settings->value("tabsMidButton", true).toBool());
		if (lang != AppFactory::settings->value("currentLanguage", "rssguard_en.qm").toString())
			QMessageBox::information(this, tr("Language changed"), tr("Language of RSSG has changed. Please restart application to changes to take effect."));
	}
}

void FormMain::markCategoryRead() {
	if (isCategorySelected()) {
		QString categoryName = ui->treeFeeds->selectedItems().at(0)->text(0);
		QList<RssFeed*> listFeeds = reader->getFeedsByCategory(categoryName);
		foreach (RssFeed *feed, listFeeds) {
			feed->markAllVisibleMessages(true);
		}

		ui->treeMessages->clear();
		foreach (RssFeed *feed, listFeeds) {
			feed->updateRepresentation(ui->treeMessages, true);
		}
		emit listNeedsRefresh();
	}
}

void FormMain::markCategoryUnread() {
	if (isCategorySelected()) {
		QString categoryName = ui->treeFeeds->selectedItems().at(0)->text(0);
		QList<RssFeed*> listFeeds = reader->getFeedsByCategory(categoryName);
		foreach (RssFeed *feed, listFeeds) {
			feed->markAllVisibleMessages(false);
		}

		ui->treeMessages->clear();
		foreach (RssFeed *feed, listFeeds) {
			feed->updateRepresentation(ui->treeMessages, true);
		}
		emit listNeedsRefresh();
	}
}

void FormMain::updateFeed(RssFeed *feed) {
	ui->actionQuit->setEnabled(false);
	QList<RssFeed*> list;
	list.append(feed);
	showStatus(1);
	updater->updateAsync(list, AppFactory::settings->value("updateTimeout", DEF_TIMEOUT).toInt());
}

void FormMain::updateFeed() {
	if (isFeedSelected()) {
		ui->actionQuit->setEnabled(false);
		QList<RssFeed*> list;
		list.append(reader->getFeedByName(ui->treeFeeds->selectedItems().at(0)->text(0)));
		showStatus(1);
		updater->updateAsync(list, AppFactory::settings->value("updateTimeout", DEF_TIMEOUT).toInt());
	}
	else
		updateCategory();
	emit listNeedsRefresh();
}

void FormMain::updateCategory() {
	if (isCategorySelected()) {
		QString categoryName = ui->treeFeeds->selectedItems().at(0)->text(0);
		QList<RssFeed*> list = reader->getFeedsByCategory(categoryName);
		showStatus(list.size());
		updater->updateAsync(list, AppFactory::settings->value("updateTimeout", DEF_TIMEOUT).toInt());
	}
}

void FormMain::updateAllFeeds() {
	showStatus(reader->getFeeds()->size());
	updater->updateAsync(*reader->getFeeds(), AppFactory::settings->value("updateTimeout", DEF_TIMEOUT).toInt());
}

void FormMain::evaluateNetCodeError(QNetworkReply::NetworkError code) {
	if (code == QNetworkReply::NoError)
		return;

	QString answer = AppFactory::getNetCodeError(code);
	if (isVisible())
		QMessageBox::critical(this, tr("Error"), answer);
	else
		tray->showMessage(tr("Error"), answer, QSystemTrayIcon::Critical, 4000);
}
